﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XCSerialPort
{
    public class SerialPort_CH341_I2CManager : ISerialPortManager
    {
        private UInt32 mIndex; // 设备索引
        private bool m_open; // 设备打开状态
        private uint deviceID = 0; // 当前设备 ID
        public SerialPort_CH341_I2CManager() { }

        public SerialPort_CH341_I2CManager(UInt32 iIndex)
        {
            mIndex = iIndex; // 初始化设备索引
        }

        public void SetDeviceConfig(int iIndex, uint _deviceID)
        {
            deviceID = _deviceID;
            mIndex = (uint)iIndex; // 初始化设备索引
        }
        /// <summary>
        /// 关闭设备，并返回状态消息。
        /// </summary>
        /// <param name="msg">状态消息。</param>
        public void Close(out string msg)
        {
            if (m_open)
            {
                USBIOXdll.USBIO_CloseDevice(mIndex);
                m_open = false;
                msg = "设备已成功关闭";
            }
            else
            {
                msg = "设备未打开，无法关闭";
            }
        }

        /// <summary>
        /// 尝试打开设备并返回状态消息。
        /// </summary>
        /// <param name="msg">状态消息。</param>
        /// <returns>打开成功返回 true，失败返回 false。</returns>
        public bool Open(out string msg)
        {
            var mid = USBIOXdll.USBIO_OpenDevice(mIndex);
            if (mid != IntPtr.Zero) // 使用 IntPtr.Zero 来代表无效句柄
            {
                m_open = true;
                msg = "设备已成功打开";
                return true;
            }
            else
            {
                m_open = false;
                msg = "设备打开失败，请检查连接";
                return false;
            }
        }


        /// <summary>
        /// 从指定的寄存器地址读取多个数据值（以 ulong 为单位）。
        /// </summary>
        /// <param name="addr">寄存器地址。</param>
        /// <param name="msg">输出参数，返回状态消息。</param>
        /// <param name="length">要读取的值的数量，默认为 1。</param>
        /// <param name="timeoutMilliseconds">读取超时的时间（毫秒），默认为 150 毫秒。</param>
        /// <returns>返回读取到的值列表。</returns>
        public List<ulong> ReadRegister(ulong addr, out string msg, int length = 1, int timeoutMilliseconds = 150)
        {
            msg = "读取寄存器成功"; // 默认成功消息
            List<ulong> values = new List<ulong>();

            if (!m_open)
            {
                msg = "设备未打开，无法读取数据";
                return values; // 返回空值列表
            }

            // 检查地址是否有效
            if (addr < 0 || addr > 0xFFFFFF)
            {
                msg = "无效的寄存器地址";
                return values; // 返回空值列表
            }

            // 读取数据
            byte[] readData;
            if (GetI2CData((uint)addr, (byte)deviceID, length, out readData))
            {
                // 将读取的数据转换为 ulong 并添加到列表
                for (int i = 0; i < length; i++)
                {
                    values.Add(readData[i]); // 假设每个读取的值都是 uint 类型，需根据实际情况调整
                    msg += $"\nR: 0x{addr + (ulong)i:X8}, 0x{readData[i]:X2}"; // 更新状态消息
                }
            }
            else
            {
                msg = "读取寄存器失败"; // 更新失败消息
            }

            return values; // 返回读取到的值
        }

        /// <summary>
        /// 向指定的寄存器地址写入多个数据值（以 ulong 为单位）。
        /// </summary>
        /// <param name="addr">寄存器地址。</param>
        /// <param name="value">要写入的值列表。</param>
        /// <param name="msg">输出参数，返回状态消息。</param>
        /// <param name="length">写入的长度，默认为 1。</param>
        /// <param name="timeoutMilliseconds">写入超时的时间（毫秒），默认为 150 毫秒。</param>
        public void WriteRegister(ulong addr, List<ulong> value, out string msg, int length = 1, int timeoutMilliseconds = 150)
        {
            msg = "写入寄存器成功"; // 默认成功消息

            if (!m_open)
            {
                msg = "设备未打开，无法写入数据";
                return; // 退出方法
            }

            // 检查输入值数量
            if (value.Count < length)
            {
                msg = "提供的值数量不足";
                return; // 退出方法
            }

            // 将值转换为可写入格式
            List<uint> dataToSend = new List<uint>();
            for (int i = 0; i < length; i++)
            {
                dataToSend.Add((uint)value[i]); // 转换为 uint，需根据实际情况调整
            }

            // 发送数据
            if (!SendI2CData((uint)addr, dataToSend, (byte)deviceID))
            {
                msg = "写入寄存器失败"; // 更新消息
            }
            else
            {
                // 更新消息以包含写入的地址和值
                msg += $"\nW: 0x{addr:X8}, 数据: ";
                foreach (var val in value)
                {
                    msg += $"0x{val:X2} "; // 格式化为16进制
                }
            }
        }


        /// <summary>
        /// 发送 I2C 数据
        /// </summary>
        /// <param name="subB">用于构建 I2C 数据的参数</param>
        /// <param name="data">要发送的数据</param>
        /// <param name="deviceNo">设备编号</param>
        /// <returns>成功发送返回 true，失败返回 false</returns>
        public bool SendI2CData(uint subB, List<uint> data, byte deviceNo)
        {
            if (subB > 0xFFFFFFFF)
            {
                return false; // 超过范围
            }

            if (subB <= 0xFFFF)
            {
                if (data.Count == 0) return false; // 确保有数据可发送

                byte[] wbuffer = new byte[4];
                wbuffer[0] = deviceNo;
                wbuffer[1] = Convert.ToByte((subB >> 8) & 0xff);
                wbuffer[2] = Convert.ToByte(subB & 0xff);
                wbuffer[3] = Convert.ToByte(data[0]); // 发送第一个数据

                return USBIOXdll.USBIO_StreamI2C(mIndex, Convert.ToUInt16(wbuffer.Length), wbuffer, 0, null);
            }

            // 发送高位字节
            if (!SendI2CSubBByte(deviceNo, (byte)((subB >> 24) & 0xff), 0xfd)) return false;
            if (!SendI2CSubBByte(deviceNo, (byte)((subB >> 16) & 0xff), 0xfe)) return false;

            byte[] wbufferFinal = new byte[3 + data.Count];
            wbufferFinal[0] = deviceNo;
            wbufferFinal[1] = Convert.ToByte((subB >> 8) & 0xff);
            wbufferFinal[2] = Convert.ToByte(subB & 0xff);

            for (int i = 0; i < data.Count; i++)
            {
                wbufferFinal[3 + i] = Convert.ToByte(data[i]);
            }

            return USBIOXdll.USBIO_StreamI2C(mIndex, Convert.ToUInt16(wbufferFinal.Length), wbufferFinal, 0, null);
        }

        /// <summary>
        /// 获取 I2C 数据
        /// </summary>
        /// <param name="subB">用于构建 I2C 数据的参数</param>
        /// <param name="deviceNo">设备编号</param>
        /// <param name="readLength">要读取的数据长度</param>
        /// <param name="readData">输出读取的数据</param>
        /// <returns>成功获取返回 true，失败返回 false</returns>
        public bool GetI2CData(uint subB, byte deviceNo, int readLength, out byte[] readData)
        {
            readData = null;

            if (readLength <= 0) return false;

            if (subB <= 0xFFFF)
            {
                byte[] wbuffer = new byte[3];
                byte[] rbuffer = new byte[readLength];

                wbuffer[0] = deviceNo;
                wbuffer[1] = Convert.ToByte((subB >> 8) & 0xff);
                wbuffer[2] = Convert.ToByte(subB & 0xff);

                if (USBIOXdll.USBIO_StreamI2C(mIndex, Convert.ToUInt32(wbuffer.Length), wbuffer, Convert.ToUInt32(rbuffer.Length), rbuffer))
                {
                    readData = rbuffer;
                    return true;
                }
                return false;
            }

            // 发送高位字节
            if (!SendI2CSubBByte(deviceNo, (byte)((subB >> 24) & 0xff), 0xfd)) return false;
            if (!SendI2CSubBByte(deviceNo, (byte)((subB >> 16) & 0xff), 0xfe)) return false;

            byte[] wbuffer32 = new byte[3];
            byte[] rbuffer32 = new byte[readLength];

            wbuffer32[0] = deviceNo;
            wbuffer32[1] = Convert.ToByte((subB >> 8) & 0xff);
            wbuffer32[2] = Convert.ToByte(subB & 0xff);

            if (USBIOXdll.USBIO_StreamI2C(mIndex, Convert.ToUInt32(wbuffer32.Length), wbuffer32, Convert.ToUInt32(rbuffer32.Length), rbuffer32))
            {
                readData = rbuffer32;
                return true;
            }
            return false;
        }

        /// <summary>
        /// 发送 I2C 子字节
        /// </summary>
        private bool SendI2CSubBByte(byte deviceNo, byte dataByte, byte controlByte)
        {
            byte[] fdbuf = new byte[4]
            {
        deviceNo,
        0xff,
        controlByte,
        dataByte
            };

            return USBIOXdll.USBIO_StreamI2C(mIndex, Convert.ToUInt16(fdbuf.Length), fdbuf, 0, null);
        }



        /// <summary>
        /// 获取可用串口
        /// </summary>
        /// <param name="msg">状态消息</param>
        /// <returns>可用的串口名称数组</returns>
        public string[] GetSerialPorts(out string msg)
        {
            List<string> ports = new List<string>(); // 使用 List 来动态添加串口
            msg = ""; // 初始化消息内容

            for (uint i = 0; i < 0xff; i++)
            {
                uint mwlen = 3; // 写长度
                uint mrlen = 1; // 读长度

                byte[] wbuffer = new byte[3];
                wbuffer[0] = (byte)i; // 设备号
                wbuffer[1] = 0; // 控制字节
                wbuffer[2] = 0; // 控制字节
                byte[] rbuffer = new byte[1]; // 读取缓冲区

                // 发送 I2C 流命令
                if (USBIOXdll.USBIO_StreamI2C(mIndex, mwlen, wbuffer, mrlen, rbuffer))
                {
                    if (rbuffer[0] != 0xff && (i % 2) == 0) // 假设有效设备的判断条件
                    {
                        ports.Add($"0x{i:X2}"); // 将有效设备添加到列表
                        msg += $"搜索到设备：0x{i:X2}\r\n"; // 更新状态消息
                    }
                }
            }

            return ports.ToArray(); // 返回串口名称数组
        }

        /// <summary>
        /// 获取所有连接的 CH341 设备的信息，包括设备索引和设备名称。
        /// </summary>
        /// <param name="message">输出状态消息，指示找到的设备数量或错误信息。</param>
        /// <returns>包含所有 CH341 设备信息的字符串列表，格式为“设备索引: X, 设备名称: Y”。</returns>
        public List<string> GetAllCH341Devices(out string message)
        {
            List<string> deviceList = new List<string>(); // 存储找到的设备信息
            message = ""; // 初始化消息内容
            int deviceCount = 0; // 连接的设备数量

            // 遍历可能的 CH341 设备索引，最大数量由 USBIOXdll.mCH341_MAX_NUMBER 定义
            for (UInt32 i = 0; i < USBIOXdll.mCH341_MAX_NUMBER; i++)
            {
                IntPtr deviceNamePtr = USBIOXdll.USBIO_GetDeviceName(i); // 获取设备名称的指针
                if (deviceNamePtr != IntPtr.Zero)
                {
                    // 转换 IntPtr 为字符串
                    string deviceName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(deviceNamePtr);
                    deviceList.Add($"设备索引: {i}, 设备名称: {deviceName}"); // 添加设备信息到列表
                    deviceCount++; // 增加找到的设备计数
                }
                else
                {
                    // 如果设备没有名称，可能是设备不再有效，结束循环
                    break;
                }
            }

            // 根据找到的设备数量设置消息内容
            if (deviceCount == 0)
            {
                message = "未找到任何 CH341 设备。"; // 没有设备连接
            }
            else
            {
                message = $"找到 {deviceCount} 个 CH341 设备。"; // 返回找到的设备数量
            }

            return deviceList; // 返回包含设备信息的列表
        }


    }
}
